/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package earthdawnnet.server;

import earthdawnnet.events.client.ClientEvent;
import earthdawnnet.util.Mutex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @author Andreas Franek
 */
public class Server extends Thread
{
    // <editor-fold defaultstate="collapsed" desc="Start/stop mechanisms">
    ServerSocket soc;
    private boolean doLoop = true;
    private IEarthdawnServer eds;
    
    public Server(IEarthdawnServer eds) throws IOException
    {
        super();
        ip = getLocalIP();
        port = getPortFromUser();
        this.eds = eds;
        System.out.println("Listening on "+ ip+":"+port);
        soc = new ServerSocket(port, 0, InetAddress.getByName(ip));
    }

    @Override
    public void run()
    {
        while(doLoop)
        {
            try
            {
                Socket connection = soc.accept();
                int id = addNInc();
                ClientHandler ch = new ClientHandler(connection, eds, id);
                clients.put(id, ch);
                ch.start();
                //System.out.println("Client with ID " + id + " registered");
            } catch (IOException ex)
            {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                close();
            }
        }
    }
    
    public void close()
    {
        doLoop = false;
        for(ClientHandler ch : clients.values())
        {
            ch.close();
        }
        try
        {
            soc.close();
        } catch (IOException ex)
        {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    //</editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="IP and port functionality">
    public static final int earthdawnNetPort = 5614; // EDN in numbers :D
    
    private static final String myIPURL = "http://automation.whatismyip.com/n09230945.asp";

    private int port;
    
    private String ip;
    
    private static String wanIP = "";

    public int getPort()
    {
        return port;
    }
    
    public String getIP()
    {
        return ip;
    }
    
    private int getPortFromUser()
    {
        int tmpPort = earthdawnNetPort;
        String portSt = JOptionPane.showInputDialog("Enter server port", tmpPort);
        if (portSt != null) {
            tmpPort =  Integer.valueOf(portSt);
        }
        return tmpPort;
    }

    public static String getWANIP()
    {
        if(wanIP.equals(""))
        {
            BufferedReader br = null;
            try
            {
                br = new BufferedReader(new InputStreamReader(new URL(myIPURL).openStream()));
                wanIP = br.readLine();
            } catch (IOException ex)
            {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            } finally
            {
                try
                {
                    br.close();
                } catch (IOException ex)
                {
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return wanIP;
    }

    private static String getLocalIP()
    { 
        String result = "";
        try
        {
            InetAddress in = InetAddress.getLocalHost();  
            InetAddress[] all = InetAddress.getAllByName(in.getHostName());
            LinkedList<String> ads = new LinkedList<String>();
            for(InetAddress ia : all)
            {
                if(ia instanceof Inet4Address) ads.add(ia.getHostAddress());
            }
            ads.add("127.0.0.1");
            result = (String)JOptionPane.showInputDialog(null, "Choose the network you want to run the server on", "Choose A Network", JOptionPane.PLAIN_MESSAGE, null, ads.toArray(), ads.getFirst());
        } catch (UnknownHostException ex)
        {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(0);
        }
        return result;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Server description">
    public static final String serverName = "EarthdawnNetServer";
    public static String getServerDesc()
    {
        return serverName;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="ClientHandler management">
    private ConcurrentMap<Integer, ClientHandler> clients = new ConcurrentSkipListMap<Integer, ClientHandler>();
    
    private int clientCounter = 0;
    
    private Mutex clientMutex = new Mutex();
    
    private int addNInc()
    {
        int result;
        clientMutex.acquire();
        result = clientCounter++;
        clientMutex.release();
        return result;
    }
    
    public void remove(int id)
    {
        clients.get(id).close();
        clients.remove(id);
    }
    
    public void broadcast(ClientEvent ce)
    {
        for(ClientHandler ch : clients.values())
        {
            ch.send(ce);
        }
    }
    
    public void send(int receiver, ClientEvent ce)
    {
        //System.out.println("Trying to send a message to client " + receiver);
        //if(clients == null) System.out.println("Client storage was null");
        //if(clients.get(receiver) == null) System.out.println("Client was null");
        clients.get(receiver).send(ce);
    }
    // </editor-fold>
}
